// Package paginator 处理分页逻辑
package paginator

import (
	"fmt"
	"gohub/pkg/config"
	"gohub/pkg/helpers"
	"gohub/pkg/logger"
	"math"
	"net/url"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/spf13/cast"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

// Config 分页配置
type Config struct {
	PageParam      string // 页码参数名
	PerPageParam   string // 每页数量参数名
	SortParam      string // 排序字段参数名
	OrderParam     string // 排序顺序参数名
	DefaultPerPage int    // 默认每页数量
	MaxPerPage     int    // 每页最大数量
}

// DefaultConfig 默认配置
var DefaultConfig = Config{
	PageParam:      "page",
	PerPageParam:   "per_page",
	SortParam:      "sort",
	OrderParam:     "order",
	DefaultPerPage: 15,
	MaxPerPage:     100,
}

// Paging 分页数据
type Paging struct {
	CurrentPage int    // 当前页
	PerPage     int    // 每页条数
	TotalPage   int    // 总页数
	TotalCount  int64  // 总条数
	NextPageURL string // 下一页的链接
	PrevPageURL string // 上一页的链接
	Sort        string // 排序规则
	Order       string // 排序顺序
}

// Paginator 分页操作类
type Paginator struct {
	config      Config
	baseURL     string // 用以拼接 URL
	queryParams url.Values
	db          *gorm.DB
	query       *gorm.DB     // db query 句柄
	ctx         *gin.Context // gin context，方便调用
}

// New 创建分页器实例
func New(c *gin.Context, db *gorm.DB, baseURL string, config ...Config) *Paginator {
	p := &Paginator{
		db:      db,
		baseURL: baseURL,
	}

	// 设置配置
	if len(config) > 0 {
		p.config = config[0]
	} else {
		p.config = DefaultConfig
	}

	// 解析URL参数
	p.queryParams = c.Request.URL.Query()
	return p
}

// Paginate 分页
// c —— gin.context 用来获取分页的 URL 参数
// db —— GORM 查询句柄，用以查询数据集和获取数据总数
// baseURL —— 用以分页链接
// data —— 模型数组，传址获取数据
// PerPage —— 每页条数，优先从 url 参数里取，否则使用 perPage 的值
// 用法:
//
//	   query := database.DB.Model(Topic{}).Where("category_id = ?", cid)
//	var topics []Topic
//	   paging := paginator.Paginate(
//	       c,
//	       query,
//	       &topics,
//	       app.APIURL(database.TableName(&Topic{})),
//	       perPage,
//	   )
func (p *Paginator) Paginate(data interface{}) Paging {

	// 获取分页参数
	page := p.getCurrentPage()
	perPage := p.getPerPage()
	sort := p.getSort()
	order := p.getOrder()

	// 计算总数
	var totalCount int64
	if err := p.db.Count(&totalCount).Error; err != nil {
		return Paging{}
	}

	// 计算总页数
	totalPage := p.getTotalPage(totalCount, perPage)

	// 执行查询
	offset := (page - 1) * perPage
	err := p.db.
		Preload(clause.Associations).
		Order(clause.OrderByColumn{
			Column: clause.Column{Name: sort},
			Desc:   strings.ToLower(order) == "desc",
		}).
		Limit(perPage).
		Offset(offset).
		Find(data).Error

	// 数据库出错
	if err != nil {
		logger.LogIf(err)
		return Paging{}
	}

	// 构建分页结果
	return Paging{
		CurrentPage: page,
		PerPage:     perPage,
		TotalPage:   totalPage,
		TotalCount:  totalCount,
		NextPageURL: p.getPageLink(page+1, totalPage),
		PrevPageURL: p.getPageLink(page-1, totalPage),
		Sort:        sort,
		Order:       order,
	}
}

// getCurrentPage 获取当前页码
func (p *Paginator) getCurrentPage() int {
	// 优先取用户请求的 page
	page := p.queryParams.Get(config.Get("paging.url_query_page"))
	if page == "" {
		// 默认为 1
		return 1
	}
	return helpers.Max(cast.ToInt(page), 1)
}

// 获取每页数量
func (p *Paginator) getPerPage() int {
	// 优先使用请求 per_page 参数
	queryPerpage := p.queryParams.Get(config.Get("paging.url_query_per_page"))
	if queryPerpage == "" {
		return config.GetInt("paging.perpage")
	}

	perPage := cast.ToInt(queryPerpage)

	// 没有传参，使用默认
	if perPage <= 1 {
		return config.GetInt("paging.perpage")
	}

	// 不超过最大限制
	if perPage > config.GetInt("paging.max_per_page") {
		return config.GetInt("paging.max_per_page")
	}

	return perPage
}

// 获取排序字段
func (p *Paginator) getSort() string {
	if sort := p.queryParams.Get(p.config.SortParam); sort != "" {
		return sort
	}
	return "id"
}

// 获取排序顺序
func (p *Paginator) getOrder() string {
	order := strings.ToLower(p.queryParams.Get(p.config.OrderParam))
	if order == "asc" || order == "desc" {
		return order
	}
	return "asc"
}

// getTotalCount 返回的是数据库里的条数
func (p *Paginator) getTotalCount() int64 {
	var count int64
	if err := p.query.Count(&count).Error; err != nil {
		return 0
	}
	return count
}

// getTotalPage 计算总页数
func (p *Paginator) getTotalPage(totalCount int64, perPage int) int {
	if totalCount == 0 || perPage == 0 {
		return 0
	}
	nums := int64(math.Ceil(float64(totalCount) / float64(perPage)))
	if nums == 0 {
		nums = 1
	}
	return int(nums)
}

// 兼容 URL 带与不带 `?` 的情况
func (p *Paginator) formatBaseURL(baseURL string) string {
	if strings.Contains(baseURL, "?") {
		baseURL = baseURL + "&" + config.Get("paging.url_query_page") + "="
	} else {
		baseURL = baseURL + "?" + config.Get("paging.url_query_page") + "="
	}
	return baseURL
}

// 拼接分页链接
func (p *Paginator) getPageLink(page, totalPage int) string {
	if page < 1 || (totalPage > 0 && page > totalPage) {
		return ""
	}

	// 复制查询参数
	params := make(url.Values)
	for k, v := range p.queryParams {
		params[k] = v
	}

	// 更新分页参数
	params.Set(p.config.PageParam, cast.ToString(page))
	params.Set(p.config.PerPageParam, cast.ToString(p.getPerPage()))
	params.Set(p.config.SortParam, p.getSort())
	params.Set(p.config.OrderParam, p.getOrder())

	// 构建URL
	sep := "?"
	if strings.Contains(p.baseURL, "?") {
		sep = "&"
	}
	return fmt.Sprintf("%s%s%s", p.baseURL, sep, params.Encode())
}
